// Copyright (c) 2010 Manuel Peinado Gallego <manuel.peinado@gmail.com>
// Distributed under the MIT license


// Action.cpp : implementation of the Action class
//
#include "stdafx.h"
#include "WorldBuilder.h"
#include "Action.h"
#include "NodeManager.h"
#include "SelectionManager.h"
#include "GeometryNode.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

Action::Action() 
{
}

void Action::perform(World& w) 
{ 
    perform(w); 
}

void Action::revert(World& w) 
{ 
    revert(w); 
}


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

RemoveAllNodesAction::RemoveAllNodesAction(NodeManager& nm) 
: m_nodeManager(nm)
{
}

void RemoveAllNodesAction::perform(World& w)
{
    m_treeCopy = w.getNodeManager().getNodes();
    
    m_nodeManager.clear();
}

void RemoveAllNodesAction::revert(World&)
{
    m_nodeManager.appendTree(m_treeCopy);
}

std::string RemoveAllNodesAction::description() const
{
    return "Remove all nodes";
}


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

TransformNodeAction::TransformNodeAction(
    ref<TransformNode> item, const gx::Mat4& dest, NodeEvent::Property prop)
: PropertyAction(item, dest), m_changedProp(prop)
{
}

TransformNodeAction::TransformNodeAction(
    ref<TransformNode> item, const gx::Mat4& dest, 
    const gx::Mat4& iniMat, NodeEvent::Property prop)
 : PropertyAction(item, dest, iniMat), m_changedProp(prop)
{
}

std::string TransformNodeAction::description() const
{
    return "Transform block action";
}

void TransformNodeAction::setValue(const gx::Mat4& dest) const 
{
    m_item->setLocalTransform(dest, m_changedProp);
}

gx::Mat4 TransformNodeAction::getValue() const
{
    return m_item->getLocalTransform();
}


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

/*
ReplaceShapeAction::ReplaceShapeAction(const Block & block, ref<Shape> newShape)
: m_block(block), m_newShape(newShape)
{
}

std::string ReplaceShapeAction::description() const
{
    return "Replace shape action";
}

void ReplaceShapeAction::perform(NodeManager & nodeManager)  //
{
    m_previousShape = nodeManager.getShape(m_block)->clone();

    nodeManager.replaceShape(m_block, m_newShape);
}

void ReplaceShapeAction::revert(NodeManager & nodeManager)  //
{
    nodeManager.replaceShape(m_block, m_previousShape);
}
*/

//InsertBlockAction::InsertBlockAction(ref<Node> block)
//: m_block(block)
//{
//}
//
//std::string InsertBlockAction::description() const
//{
//    return "Insert block action";
//}

// TODO this assumes that insert operations are not
// destructive (i.e., that the destination block was
// not previously occupied). If that is not the case
// we should store the previous shape and restore it
// upon revert.
// Once we have done that, we can implement EraseBlockAction
// as the "inverse" of InsertBlockAction

//void InsertBlockAction::perform(World& )  //
//{
//    //nodeManager.appendNode(nodeManager.getNodes().begin()->get(), m_block);
//
//    //SelectionManager::instance().select(m_block.get());
//}
//
//void InsertBlockAction::revert(World& )  //
//{
//    // TODO complete this
//    //nodeManager.eraseBlock(m_block);
//    //SelectionManager::instance().selectNothing();
//}

/*
EraseBlockAction::EraseBlockAction(const Block & block)
: m_block(block)
{
}

std::string EraseBlockAction::description() const
{
    return "Erase block action";
}

void EraseBlockAction::perform(NodeManager & nodeManager)  //
{
    m_previousShape = nodeManager.getShape(m_block)->clone();

    nodeManager.eraseBlock(m_block);
    
    SelectionManager::instance().selectNothing();
}

void EraseBlockAction::revert(NodeManager & nodeManager)  //
{
    nodeManager.insertBlock(m_block, m_previousShape);
    
    SelectionManager::instance().selectNode(m_block);
}
*/

ShapeWireframeAction::ShapeWireframeAction(
    ref<MeshBufferInstance> item, bool  v)
: PropertyAction(item, v)
{
}

std::string ShapeWireframeAction::description() const
{
    return "Shape node wireframe action";
}

void ShapeWireframeAction::setValue(bool v) const
{
    m_item->setWireframe(v);
}

bool ShapeWireframeAction::getValue() const
{
    return m_item->isWireframe();
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

ShapeMaterialIdAction::ShapeMaterialIdAction(
    ref<MeshBufferInstance> item, ref<Material> id, GeometrySide side)
: PropertyAction(item, id), m_side(side)
{
    ASSERT(m_side != GeometrySides::Both); // Can't undo that
}

std::string ShapeMaterialIdAction::description() const
{
    return "Shape node Material Id action";
}

void ShapeMaterialIdAction::setValue(ref<Material> v) const
{
    if(m_side == GeometrySides::Front)
    {
        m_item->setFrontMaterial(v);
        return;
    }
    m_item->setBackMaterial(v);
}

ref<Material> ShapeMaterialIdAction::getValue() const
{
    Material* m = m_side == GeometrySides::Front ?
                    m_item->getFrontMaterial() : m_item->getBackMaterial();
    return m->getRef<Material>();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

ShapeShaderIdAction::ShapeShaderIdAction(ref<MeshBufferInstance> item, 
                                         ref<ShaderProgram> v)
: PropertyAction(item, v)
{
}

std::string ShapeShaderIdAction::description() const
{
    return "Shape node Shader Id action";
}

void ShapeShaderIdAction::setValue(ref<ShaderProgram> v) const
{
    m_item->setShaderProgram(v);
}

ref<ShaderProgram> ShapeShaderIdAction::getValue() const
{
    return m_item->getShaderProgram()->getRef<ShaderProgram>();
}


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

PrimitiveTypeAction::PrimitiveTypeAction(ref<GeometryNode> item, ref<Primitive> v)
: PropertyAction(item, v)
{
}

std::string PrimitiveTypeAction::description() const
{
    return "Shape node type action";
}

void PrimitiveTypeAction::setValue(ref<Primitive> v) const
{
    m_item->setGeometry(v->clone());
}

ref<Primitive> PrimitiveTypeAction::getValue() const
{
    ref<Primitive> prim 
        = dyn_ptr_cast<Primitive>(m_item->getGeometry()->clone());
    return prim;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

LightParamsAction::LightParamsAction(ref<LightNode> item, 
                                   const LightParams& v)
: PropertyAction(item, v)
{
}

std::string LightParamsAction::description() const
{
    return "Light params action";
}

void LightParamsAction::setValue(const LightParams& v) const
{
    m_item->setParams(v);
}

LightParams LightParamsAction::getValue() const
{
    return m_item->getParams();
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

MaterialParamsAction::MaterialParamsAction(ref<Material> item, 
                                          const MaterialParams& v)
: PropertyAction(item, v)
{
}

std::string MaterialParamsAction::description() const
{
    return "Material params action";
}

void MaterialParamsAction::setValue(const MaterialParams& v) const
{
    m_item->setParams(v);
}

MaterialParams MaterialParamsAction::getValue() const
{
    return m_item->getParams();
}

